package com.autohammal.monitor.scheduler;

import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.autohammal.monitor.Scheduler;
import com.autohammal.monitor.scheduler.support.SchedulerTime;

public class WorkDaysScheduler implements Scheduler {
	private static final long serialVersionUID = 1561551394365451209L;
	private Set<Integer> holidays;
	public static final List<Integer> WEEK_DAY = Collections
			.unmodifiableList(Arrays.asList(Calendar.SUNDAY, Calendar.MONDAY,
					Calendar.TUESDAY, Calendar.WEDNESDAY, Calendar.THURSDAY,
					Calendar.FRIDAY, Calendar.SATURDAY));

	private SchedulerTime time = null;

	public WorkDaysScheduler() {
		holidays = new TreeSet<Integer>();
		holidays.add(Calendar.SATURDAY);
		holidays.add(Calendar.SUNDAY);
	}

	public void setHolidays(Integer... holidate) {
		if (holidate == null || holidate.length == 0
				|| holidate.length > WEEK_DAY.size())
			throw new IllegalArgumentException("Illegal weekday for holiday.");
		List<Integer> holdays = Arrays.asList(holidate);
		if (Collections.min(holdays) < Collections.min(WEEK_DAY)
				|| Collections.max(holdays) > Collections.max(WEEK_DAY)) {
			throw new IllegalArgumentException("Illegal weekday for holiday.");
		}
		holidays.clear();
		holidays.addAll(holdays);
		if (holidays.size() == WEEK_DAY.size())
			runstate = false;
		else
			runstate = true;
	}

	public void setTime(SchedulerTime time) {
		this.time = time;
	}

	private boolean isWorkDay() {
		return !holidays.contains(Calendar.getInstance().get(
				Calendar.DAY_OF_WEEK));
	}

	ExecutorService executorService = Executors.newCachedThreadPool();
	private volatile boolean runstate = true;

	@Override
	public void schedule(final Runnable runnable) {
		if (executorService == null || executorService.isShutdown()) {
			executorService = Executors.newCachedThreadPool();
		}
		executorService.execute(new Runnable() {
			@Override
			public void run() {
				while (runstate) {
					try {
						Thread.sleep(getNextTimeDelay());
						if (isWorkDay()) {
							runnable.run();
						}
					} catch (InterruptedException e) {
						Logger.getLogger(WorkDaysScheduler.class).log(
								Level.ERROR, e);
					} catch (Exception e) {
						Logger.getLogger(WorkDaysScheduler.class).log(
								Level.ERROR, e);
					}
				}

			}
		});
	}

	@Override
	public void cancel() {
		runstate = false;
		executorService.shutdownNow();
		Logger.getLogger(WorkDaysScheduler.class).log(Level.DEBUG,
				"Scheduler is shutdown");
		runstate = true;
	}

	private long getNextTimeDelay() {
		Calendar nextCal = Calendar.getInstance();
		nextCal.set(Calendar.HOUR_OF_DAY, time.getHour());
		nextCal.set(Calendar.MINUTE, time.getMinute());
		nextCal.set(Calendar.SECOND, time.getSecond());
		long delay = nextCal.getTimeInMillis() - System.currentTimeMillis();
		if (delay <= 0) {
			delay = ONE_DAY_LIGHT + delay;
		}
		return delay;
	}

}
